/*
 Authors: ZhangXuelian
 	
 
 Changes:
 	
	
*/


#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <limits.h>
#include <float.h>
#include <math.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <locale.h>
#include <sys/socket.h>

#include "coe_lib_api.h"
#include "coe_lib_types.h"

// 添加与硬件平台相关的平台适配层
#ifdef __LINUX_NVRAM__
  #include "osadapter_nvram.c"
#else
	#ifdef __LINUX_FILE__  
		pthread_mutex_t mutex;
		#include "osadapter_file.c"
	#endif
#endif

/**
 * 登录完成的通知，errcode为0表示登录成功，其余请参考全局的错误码表
 */
void on_login_complete(int errcode) {
    printf("on_login_complete | code[%d]\n", errcode);
}

/**
 * 在线状态变化通知， 状态（status）取值为 11 表示 在线， 取值为 21 表示  离线
 * old是前一个状态，new是变化后的状态（当前）
 */
void on_online_status(int old, int new) {
	printf("online status: %s\n", 11 == new ? "true" : "false"); 
}

// im 
void on_login_complete(int errcode) {
	printf("on_login_complete | code[%d]\n", errcode);
}

// mq
void on_login_complete(int errcode) {
	printf("on_login_complete | code[%d]\n", errcode);
}

/**
 * 辅助函数：SDK的log输出回调
 * SDK内部调用改log输出函数，有助于开发者调试程序
  log级别 取值有 4 严重错误；3 警告；2 提示；1 普通信息
 */
#define SDK_DEBUG_INFO		1
#define SDK_DEBUG_KEYINFO	2
#define SDK_DEBUG_WARNING	3
#define SDK_DEBUG_ERROR	    4
#define SDK_DEBUG_LEVEL     SDK_DEBUG_INFO
void log_func(int level, const char* module, int line, const char* message)
{
	if(level >= SDK_DEBUG_LEVEL)
	{
		printf("module:%s - line[%d] - %s\n",module,line, message);
	}
}

/**
 * SDK初始化
 * 例如：
 * （1）填写设备基本信息
 * （2）打算监听哪些事件，事件监听的原理实际上就是设置各类消息的回调函数，
 * 	例如设置 指令消息 回调：
 * 	开发者应该定义如下的 my_on_receive_msg 函数，将其赋值coe_msg_notify对象中对应的函数指针，并初始化：
 *
 * 			coe_msg_notify msgNotify = {0};
 * 			msgNotify.on_receive_msg = my_on_receive_msg;
 * 			coe_init_msg(&msgNotify);
 *
 * 	那么当SDK内部的一个线程收到对方发过来的指令消息后（通过服务器转发），
 *  将同步调用 msgNotify.on_receive_msg 
 */
 
bool initDevice() 
{
	// load manufacturer_license
	unsigned char manufacturer_license[256] = {0};
	int nLicenseSize = 0;
	if (!read_buffer("./manufacturer_license.txt", manufacturer_license, sizeof(manufacturer_license), &nLicenseSize)) 
    {
		printf("[error]get license from file failed...\n");
		return false;
	}

	// load device_serial_number_license
	unsigned char device_serial_number_license[32] = {0};
	int sn_size = 0;
	if(!read_buffer("./device_serial_number_license.txt", device_serial_number_license, sizeof(device_serial_number_license), &sn_size)) 
    {
		printf("[error]get guid from file failed...\n");
		return false;
	}

    // 设备的基本信息
    COE_DEVICE_INFO info = {0};
    info.os_platform = "linux";
    // 厂商信息
    info.manufacturer_id = "我是一个厂商ID";
    info.manufacturer_license = manufacturer_license;
    // 产品信息
    info.product_id = 10001;
    info.product_version = 1;
    // 设备本身信息
    info.device_name = "demo1";
    info.device_serial_number = "我是一个SN号";
    info.device_serial_number_license = device_serial_number_license;
    info.sdk_type = 1;
	info.test_mode = 1;     
        
	// 设备登录、在线状态、指令消息等相关的事件通知
	// 注意事项：
	// 如下的这些notify回调函数，都是来自硬件SDK内部的一个线程，所以在这些回调函数内部的代码一定要注意线程安全问题
	// 比如在on_login_complete操作某个全局变量时，一定要考虑是不是您自己的线程也有可能操作这个变量
    COE_DEVICE_NOTIFY notify     = 
	{
		.on_online_status = on_online_status,
		.on_login_complete = on_login_complete,
	};

	COE_DEVICE_DATA_FUNC data_func = 
	{
		.write = coe_write;
		.read = coe_read;
		.delete = coe_delete;	
	};
	
    // 设置log输出函数，如果不想打印log，则无需设置。
    // 建议开发在开发调试阶段开启log，在产品发布的时候禁用log。
    coe_set_log_func(log_func);

    // 初始化SDK，若初始化成功，则内部会启动一个线程去执行相关逻辑，该线程会持续运行，直到收到 exit 调用
	int ret = coe_init_lib(&info, &notify, &data_func);
	if (0 == ret) {
		printf(" init_device success\n");
	}
	else 
	{
		printf(" init_device failed [%d]\n", ret);
		return false;
	}
	return true;
}


bool deinitDevice() 
{
	return coe_deinit_lib();
}

void on_receive_record_notify(char * json)
{
	return;
}

// 初始化指令执行模块
COE_RECORD_HANDLER * initDeviceRecord()
{
	COE_RECORD_HANDLER handler = {0};
	handler.record_notify = on_receive_record_notify;
	handler.record_send = 0; // this member do not assgin value.
	return coe_init_record_module(&handler);
}

void on_receive_cmd(int task_no, char * json)
{
	return;
}

int on_aio_cmd_send_result(int task_no, int result)
{
	;
}

// 初始化数据采集模块
COE_CMD_HANDLER * initDeviceCmd()
{
	COE_CMD_HANDLER handler = {0};
	handler.aio_cmd_receive = on_receive_cmd;
	handler.aio_cmd_send_result = on_aio_cmd_send_result;
	handler.aio_cmd_send = 0; // this member do not assgin value.
	return coe_init_cmd_module(&handler);
}

/****************************************************************
*  测试代码：
*
*  （1）while循环的作用仅仅是使 Demo进程不会退出，实际使用SDK时一般不需要
*  （2） 输入 "exit" 将会退出当前进程，这段逻辑存在的原因在于：
*     					在某些芯片上，直接用Ctrl+C 退出易产生僵尸进程
*  （3）while循环里面的sleep(1)在这里是必须的，因为如果Demo进程后台运行，
*   scanf没有阻塞作用，会导致当前线程跑满CPU
*
*****************************************************************/
int main(int argc, char **argv) 
{
#ifdef __LINUX_FILE__
	pthread_mutex_init(&mutex, NULL);
#endif
	if ( !initDevice() ) 
    {
		return -1;
	}
    
	COE_CMD_HANDLER * cmd_handler = initDeviceCmd();
	COE_RECORD_HANDLER * record_handler = initDeviceRecord();
	

	// user do something.
	// 发送指令的结果给服务器。
	cmd_handler->cmd_send(GetTickCount(),"",1);
	// 发送采集记录给服务器。
	record_handler->record_send("json",3,0,1);


	// ...
	// 你可以在这里做其他相关的事情
	// ...
	char input[10] = {0};
	while (scanf("%s", input)) 
    {
		if ( !strcmp(input, "exit") ) 
        {
			deinitDevice();
			break;
		}
		sleep(1);
	}
#ifdef __LINUX_FILE__
	pthread_mutex_destroy(&mutex);
#endif
	return 0;
}

/* The End */
